use std::env;
use std::path::PathBuf;

use attestation_common::log::{error, info};
use crate::policy_error::policy_error::PolicyError;


/// A utility struct for handling policy-related configuration operations
pub struct PolicyUtil;

impl PolicyUtil {
    /// The default path for the YAML configuration file
    const YAML_CONFIG_FILE_PATH: &'static str = "server_config.yaml";

    /// Retrieves a boolean value from the YAML configuration file
    ///
    /// # Arguments
    /// * `config_key` - The key to look up in the configuration file
    /// * `default_value` - The default value to return if the key is not found
    ///
    /// # Returns
    /// Returns the boolean value from the configuration or the default value if not found
    pub fn get_bool_yaml_config(config_key: &str, default_value: bool) -> bool {
        let yaml = match Self::get_yaml(default_value) {
            Ok(value) => value,
            Err(value) => return value,
        };
        let value = attestation_common::yaml_get!(yaml.clone(), config_key => bool);
        match value {
            Some(val) => {
                info!("Configuration item '{}' exist in yaml file, use '{}'",config_key, val);
                val
            },
            None => {
                error!("Configuration item '{}' does not exist in yaml file, use default value",config_key);
                default_value
            }
        }
    }

    /// Retrieves a string value from the YAML configuration file
    ///
    /// # Arguments
    /// * `config_key` - The key to look up in the configuration file
    /// * `default_value` - The default value to return if the key is not found
    ///
    /// # Returns
    /// Returns the string value from the configuration or the default value if not found
    pub fn get_str_yaml_config(config_key: &str, default_value: String) -> String {
        let yaml = match Self::get_yaml(&default_value) {
            Ok(value) => value,
            Err(value) => return value.to_string(),
        };
        let value = attestation_common::yaml_get!(yaml.clone(), config_key => str);
        match value {
            Some(val) => {
                info!("Configuration item '{}' exist in yaml file, use '{}'",config_key, val);
                val
            },
            None => {
                error!("Configuration item '{}' does not exist in yaml file, use default value",config_key);
                default_value
            }
        }
    }

    /// Retrieves a u64 value from the YAML configuration file
    ///
    /// # Arguments
    /// * `config_key` - The key to look up in the configuration file
    /// * `default_value` - The default value to return if the key is not found
    ///
    /// # Returns
    /// Returns the u64 value from the configuration or the default value if not found
    pub fn get_u64_yaml_config(config_key: &str, default_value: u64) -> u64 {
        let yaml = match Self::get_yaml(default_value) {
            Ok(value) => value,
            Err(value) => return value,
        };
        let value = attestation_common::yaml_get!(yaml.clone(), config_key => u64);
        match value {
            Some(val) => {
                info!("Configuration item '{}' exist in yaml file, use '{}'",config_key, val);
                val
            },
            None => {
                error!("Configuration item '{}' does not exist in yaml file, use default value",config_key);
                default_value
            }
        }
    }

    /// Retrieves a string value from a map in the YAML configuration file
    ///
    /// # Arguments
    /// * `config_key` - The key to look up in the configuration file
    /// * `default_value` - The default value used for error handling
    /// * `attester_type` - The specific key to look up in the map
    ///
    /// # Returns
    /// Returns Ok with the string value if found, or Err with PolicyError if not found or invalid
    pub fn get_map_yaml_config(config_key: &str, default_value: String, attester_type: &str) -> Result<String, PolicyError> {
        let yaml = match Self::get_yaml(default_value) {
            Ok(value) => value,
            Err(_) => return Err(PolicyError::PolicyNotFoundError("config yaml error!".to_string())),
        };
        let value = attestation_common::yaml_get!(yaml.clone(), config_key => map);
        match value {
            Some(val) => {
                match val.get(attester_type) {
                    Some(v) => {
                        match v.as_str() {
                            Some(content) => Ok(content.to_string()),
                            None => Err(PolicyError::PolicyNotFoundError(format!("Value for {} is not a string", attester_type)))
                        }
                    },
                    None => Err(PolicyError::PolicyNotFoundError(format!("Key {} not found in map", attester_type)))
                }
            },
            None => {
                error!("Configuration item '{}' does not exist in yaml file", attester_type);
                Err(PolicyError::PolicyNotFoundError(format!("Configuration item '{}' not found", attester_type)))
            }
        }
    }

    /// Internal helper function to load and parse the YAML configuration file
    ///
    /// # Arguments
    /// * `default_value` - The default value to return if the YAML file cannot be loaded or parsed
    ///
    /// # Returns
    /// Returns Ok with the parsed YAML value if successful, or Err with the default value if failed
    fn get_yaml<T>(default_value: T) -> Result<attestation_common::YamlValue, T> {
        let config_path = Self::get_config_file_path().unwrap_or_else(|e| {
            error!("Failed to get config file path, using default value: {}", e);
            PathBuf::from(Self::YAML_CONFIG_FILE_PATH)
        });
        let yaml = match attestation_common::YamlValue::from_file(config_path.to_str().unwrap()) {
            Ok(yaml) => yaml,
            Err(e) => {
                error!("Failed to parse yaml file, use default value!: {}", e);
                return Err(default_value);
            }
        };
        Ok(yaml)
    }

    /// Internal helper function to determine the configuration file path
    ///
    /// # Returns
    /// Returns Ok with the PathBuf to the configuration file, or Err with an error message if the path cannot be determined
    fn get_config_file_path() -> Result<PathBuf, String> {
        let exe_path = env::current_exe().map_err(|e| format!("Failed to get current executable path: {}", e))?;
        let exe_dir = exe_path.parent().ok_or("Failed to get executable directory")?;
        let config_path = exe_dir
            .parent()
            .and_then(|p| p.parent())
            .map(|p| p.join(Self::YAML_CONFIG_FILE_PATH))
            .ok_or("Failed to construct config file path")?;
        
        Ok(config_path)
    }
}